#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_DATO 256
#define MAX_ESTRUCTURAS_ANIDADAS 10

typedef enum {
	TOKEN_STRING, /*cadena de caracteres*/
	TOKEN_NUMERO, /*numero en punto flotante*/
	TOKEN_OBJETO_EMPIEZA, /*inicio de objeto*/
	TOKEN_OBJETO_TERMINA, /*fin de objeto*/
	TOKEN_ARRAY_EMPIEZA, /*inicio de array*/
	TOKEN_ARRAY_TERMINA, /*fin de array*/
	TOKEN_COMA, /*coma*/
	TOKEN_DOSPUNTOS, /*dos puntos*/
	TOKEN_TRUE, TOKEN_FALSE, TOKEN_NULL
} TipoToken;

typedef enum {
	TIPO_EMPIEZA,
	TIPO_STRING,
	TIPO_VALOR,
	TIPO_COMA,
	TIPO_DOSPUNTOS,
	TIPO_TERMINA
} TiposDatos;

/*Estructuras logicas segun el tipo
 **Objeto:	OBJ_EMPIEZA, OBJ_STRING, OBJ_DOSPUNTOS, OBJ_VALUE, OBJ_COMA o OBJ_TERMINA
 **Array:	ARRAY_EMPIEZA, ARRAY_VAL, ARRAY_COMA o ARRAY_TERMINA	*/

typedef enum {
	NonStruct, ObjStruct, ArrayStruct
} TipoEstructura;

typedef struct _estructSintactica {
	TipoEstructura estructura; /*los posibles valores de estructura_val refieren al estado en q se encuetra*/
	TiposDatos estructura_val; /*el JSON segun la estructura actual: Objecto o Array*/
} EstructuraSintactica;

typedef struct _error {
	int codigo;
	char descripcion[MAX_DATO];
} TipoErrores;

typedef struct _tSinactico {
	EstructuraSintactica estructura_Sintactica[MAX_ESTRUCTURAS_ANIDADAS];
	int pos_vEstructuraSint; /*ultima posicion usada en estructura_Sintactica[]*/
	TipoErrores ultimo_error;
} TSintactico;

typedef struct _token {
	int tipo;
	char dato[MAX_DATO];
	int pos_dato;
} Token;

/*////////////Funciones Internas////////////////*/

void ajustar_Sangria(int cantidad) {
	int i;

	for (i = 0; i < cantidad; i++) {
		printf(" ");
	}
}

void print_token(TSintactico* as, Token *token) {
	static int sangria = 0;
	static int aux = 0;
	static int nuevaEstructura[MAX_ESTRUCTURAS_ANIDADAS]; /*nuevaEstructura[] guarda la sangria existente en donde */
	static int ulPos = 0; /*comienza un objeto o un array, ulPos es un contador para  localizar al vector*/
	TipoEstructura* tipoEst;
	TiposDatos* tipoDat;

	tipoEst = &(as->estructura_Sintactica[as->pos_vEstructuraSint].estructura);
	tipoDat
			= &(as->estructura_Sintactica[as->pos_vEstructuraSint].estructura_val);
	nuevaEstructura[0] = 0;

	switch (token->tipo) {
	case (TOKEN_STRING):
		if (*tipoEst == ObjStruct && *tipoDat == TIPO_STRING) {/*Diferencia entre un string como CLAVE de objeto y uno como Valor*/
			printf("CLAVE: ");
			aux = nuevaEstructura[ulPos - 1] + strlen(token->dato) + 9;
		} else {
			printf("(String): ");
		}
		printf("%s", token->dato);
		break;
	case (TOKEN_NUMERO):
		printf("(Numero): ");
		printf("%d", atoi(token->dato));
		break;
	case (TOKEN_OBJETO_EMPIEZA): /*identifica si se trata de un objeto dentro de una estructura (es decir, si es un TIPO_VALOR)*/
		if (as->pos_vEstructuraSint > 0
				&& as->estructura_Sintactica[as->pos_vEstructuraSint - 1].estructura_val
						== TIPO_VALOR) {
			printf("(Objeto): ");
			sangria += 10;
		}
		printf("OBJETO:\n");
		sangria += 5;
		nuevaEstructura[ulPos] = aux;
		ulPos++;
		ajustar_Sangria(sangria + nuevaEstructura[ulPos - 1]);
		break;
	case (TOKEN_OBJETO_TERMINA):
		printf("\n");
		sangria -= 5;
		ajustar_Sangria(nuevaEstructura[ulPos - 1] + sangria);
		if (as->estructura_Sintactica[as->pos_vEstructuraSint].estructura_val
				== TIPO_VALOR) {
			sangria -= 10;
		}
		ulPos--;
		aux = nuevaEstructura[ulPos];
		printf("FIN OBJETO");
		break;
	case (TOKEN_ARRAY_EMPIEZA):/*identifica si se trata de un array dentro de una estructura (es decir, si es un TIPO_VALOR)*/
		if (as->pos_vEstructuraSint > 0
				&& as->estructura_Sintactica[as->pos_vEstructuraSint - 1].estructura_val
						== TIPO_VALOR) {
			printf("(Array): ");
			sangria += 9;
		}
		printf("ARRAY\n");
		sangria += 4;
		nuevaEstructura[ulPos] = aux;
		ulPos++;
		ajustar_Sangria(sangria + aux);
		break;
	case (TOKEN_ARRAY_TERMINA):
		printf("\n");
		sangria -= 4;
		ajustar_Sangria(nuevaEstructura[ulPos - 1] + sangria);
		if (as->estructura_Sintactica[as->pos_vEstructuraSint].estructura_val
				== TIPO_VALOR) {
			sangria -= 9;
		}
		ulPos--;
		aux = nuevaEstructura[ulPos];
		printf("FIN ARRAY");
		break;
	case (TOKEN_COMA):
		printf("\n");
		ajustar_Sangria(nuevaEstructura[ulPos - 1] + sangria);
		break;
	case (TOKEN_DOSPUNTOS):
		printf(": ");
		break;
	case (TOKEN_TRUE):
		printf("(Boolean): true");
		break;
	case (TOKEN_FALSE):
		printf("(Boolean): false");
		break;
	case (TOKEN_NULL):
		printf("(Null): ");
		break;
	}
}

void TSintactico_setUltimoError(TSintactico* as, int codigo,
		char descripcion[MAX_DATO]) {
	as->ultimo_error.codigo = codigo;
	strcpy(as->ultimo_error.descripcion, descripcion);
}
/*buscar_enTIPO() busca si el token enviado por el analizador lexico, concuerda con
 * alguno de los tokens posibles, segun la estructura en q se encuentre */
int buscar_enTIPO(TSintactico* as, Token* token, TiposDatos tesperado) {
	int aux = 1;

	switch (tesperado) {
	case (TIPO_EMPIEZA):
		break;
	case (TIPO_STRING):
		if (token->tipo == TOKEN_STRING) {
			aux = 0;
		} else
			TSintactico_setUltimoError(
					as,
					2111,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKEN esperado: String).");
		break;
	case (TIPO_VALOR):
		if (token->tipo == TOKEN_STRING || token->tipo == TOKEN_NUMERO
				|| token->tipo == TOKEN_OBJETO_EMPIEZA || token->tipo
				== TOKEN_ARRAY_EMPIEZA || token->tipo == TOKEN_TRUE
				|| token->tipo == TOKEN_FALSE || token->tipo == TOKEN_NULL) {
			aux = 0;
		} else
			TSintactico_setUltimoError(
					as,
					2112,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKEN esperado: Valor).");
		break;

	case (TIPO_COMA):
		if (token->tipo == TOKEN_COMA) {
			aux = 0;
		} else
			TSintactico_setUltimoError(
					as,
					2113,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKENS esperados: Coma, Estructura Empieza o Estructura Termina).");
		break;
	case (TIPO_DOSPUNTOS):
		if (token->tipo == TOKEN_DOSPUNTOS) {
			aux = 0;
		} else
			TSintactico_setUltimoError(
					as,
					2114,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKEN esperado: Dos Puntos).");
		break;
	case (TIPO_TERMINA):
		if (token->tipo == TOKEN_OBJETO_TERMINA || token->tipo
				== TOKEN_ARRAY_TERMINA) {
			aux = 0;
		} else
			TSintactico_setUltimoError(
					as,
					2115,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKEN esperado: Objeto/Array Termina).");
		break;
	}
	return aux;
}
/*esTokenPosible() determina segun la estructura en que nos encontremos,
 * si el token recibido es valido o no. Devuelve 1 de ser posible, 0 si no*/
int esTokenPosible(TSintactico* as, Token* token) {
	int aux = 1; /*1 = TRUE...es decir, ES POSIBLE*/
	EstructuraSintactica* testructura;

	testructura = &(as->estructura_Sintactica[as->pos_vEstructuraSint]);

	switch (testructura->estructura) {
	case (NonStruct):
		if ((token->tipo == TOKEN_OBJETO_EMPIEZA || token->tipo
				== TOKEN_ARRAY_EMPIEZA)) {
		} else {
			TSintactico_setUltimoError(
					as,
					2110,
					"El token recibido no se corresponde con la sintaxis (Tipo de TOKEN esperado: Objeto/Array Empieza).");
			aux = 0;
		}
		break;
	case (ObjStruct):
		if (testructura->estructura_val == TIPO_EMPIEZA) {
			if (buscar_enTIPO(as, token, TIPO_STRING) == 0) {
				testructura->estructura_val = TIPO_STRING;
			} else if (token->tipo != TOKEN_OBJETO_TERMINA)
				aux = 0;
			;
		} else if (testructura->estructura_val == TIPO_STRING) {
			if (buscar_enTIPO(as, token, TIPO_DOSPUNTOS) == 0) {
				testructura->estructura_val = TIPO_DOSPUNTOS;
			} else
				aux = 0;
		} else if (testructura->estructura_val == TIPO_DOSPUNTOS) {
			if (buscar_enTIPO(as, token, TIPO_VALOR) == 0) {
				testructura->estructura_val = TIPO_VALOR;
			} else
				aux = 0;
		} else if (testructura->estructura_val == TIPO_VALOR) {
			if (buscar_enTIPO(as, token, TIPO_COMA) == 0) {
				testructura->estructura_val = TIPO_COMA;
			} else if (token->tipo != TOKEN_OBJETO_TERMINA)
				aux = 0;
		} else if (testructura->estructura_val == TIPO_COMA) {
			if (buscar_enTIPO(as, token, TIPO_STRING) == 0) {
				testructura->estructura_val = TIPO_STRING;
			} else
				aux = 0;
		}

		break;
	case (ArrayStruct):
		if (testructura->estructura_val == TIPO_EMPIEZA) {
			if (buscar_enTIPO(as, token, TIPO_VALOR) == 0) {
				testructura->estructura_val = TIPO_VALOR;
			} else if (token->tipo != TOKEN_ARRAY_TERMINA)
				aux = 0;
			;
		} else if (testructura->estructura_val == TIPO_VALOR) {
			if (buscar_enTIPO(as, token, TIPO_COMA) == 0) {
				testructura->estructura_val = TIPO_COMA;
			} else if (token->tipo != TOKEN_ARRAY_TERMINA) {
				aux = 0;
			}
		} else if (testructura->estructura_val == TIPO_COMA) {
			if (buscar_enTIPO(as, token, TIPO_VALOR) == 0) {
				testructura->estructura_val = TIPO_VALOR;
			} else
				aux = 0;
		}
		break;
	}

	return aux;
}
/*actualizar_Estructuras() actualiza el vectore estructura_Sintactica[]
 * y su posicionador (pos_vEstructuraSint) de acuerdo con el token recibido*/
int actualizar_Estructuras(TSintactico* as, Token* token) {
	int aux = 0;

	if (token->tipo == TOKEN_OBJETO_EMPIEZA) {
		if (as->pos_vEstructuraSint < MAX_ESTRUCTURAS_ANIDADAS - 1) {
			as->pos_vEstructuraSint++;
			as->estructura_Sintactica[as->pos_vEstructuraSint].estructura = 1;
			as->estructura_Sintactica[as->pos_vEstructuraSint].estructura_val
					= 0;
		} else {
			aux = 1;
		}
	} else if (token->tipo == TOKEN_ARRAY_EMPIEZA) {
		if (as->pos_vEstructuraSint < MAX_ESTRUCTURAS_ANIDADAS - 1) {
			as->pos_vEstructuraSint++;
			as->estructura_Sintactica[as->pos_vEstructuraSint].estructura = 2;
			as->estructura_Sintactica[as->pos_vEstructuraSint].estructura_val
					= 0;
		} else {
			aux = 1;
		}
	} else if (token->tipo == TOKEN_OBJETO_TERMINA) {
		as->pos_vEstructuraSint--;
	} else if (token->tipo == TOKEN_ARRAY_TERMINA) {
		as->pos_vEstructuraSint--;
	}
	if (aux) {
		TSintactico_setUltimoError(
				as,
				2200,
				"Se ha superado la cantidad de estructuras encadenadas. Cantidad máxima permitida : 10.");
	}
	return aux;
}

/*////////////Funciones Primitivas/////////////*/
int TSintactico_Crear(TSintactico* as) {
	int i;

	as->pos_vEstructuraSint = -1;
	for (i = 0; i < MAX_ESTRUCTURAS_ANIDADAS; i++) {
		as->estructura_Sintactica[i].estructura = 0; /*inicializa sintact_estructura[] en NONSTRUCTURE*/
		as->estructura_Sintactica[i].estructura_val = 0;
	}
	as->ultimo_error.codigo = 0; /*code 0 => NO ERROR*/
	strcpy(as->ultimo_error.descripcion, "No se han producido errores.");

	return 0;
}

int TSintactico_PushToken(TSintactico* as, Token* token) {
	int aux = 0;
	if (esTokenPosible(as, token)) { /*Si es TRUE (TRUE != 0)...*/
		aux = actualizar_Estructuras(as, token);
		if (!aux)
			print_token(as, token);
	} else {
		aux = 1;
	}
	return aux;
}

int TSintactico_terminarFlujo(TSintactico* as) {
	int i;

	as->pos_vEstructuraSint = -1;
	for (i = 0; i < MAX_ESTRUCTURAS_ANIDADAS; i++) {
		as->estructura_Sintactica[i].estructura = 0; /*re-inicializa sintact_estructura[] en NONSTRUCTURE*/
		as->estructura_Sintactica[i].estructura_val = 0;
	}
	as->ultimo_error.codigo = 0; /*code 0 => NO ERROR*/
	strcpy(as->ultimo_error.descripcion, "No se han producido errores");

	return 0;
}

int TSintactico_Destruir(TSintactico* as) {
	return 0;
}

int TSintactico_getUltimoError(TSintactico* as, int* codigo, char* mensaje) {
	int i;
	*codigo = as->ultimo_error.codigo;
	for (i = 0; i <= MAX_DATO && as->ultimo_error.descripcion[i] != '\0'; i++) {
		mensaje[i] = as->ultimo_error.descripcion[i];
	}
	mensaje[i] = '\0';
	return 0;
}

